home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Text⁄Files / Suntar 1.3.2 / dehqx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-18  |  22.9 KB  |  852 lines  |  [TEXT/KAHL]

  1.  
  2. /*******************************************************************************\
  3.  
  4. hqx decoding module
  5.  
  6. part of suntar, ©1991-92 Sauro & Gabriele Speranza
  7.  
  8. This program is public domain, feel free to use it or part of it for anything
  9.  
  10. \*******************************************************************************/
  11.  
  12.  
  13. /* #include <StdFilePkg.h> */
  14.  
  15. #include <string.h>
  16.  
  17. #include "suntar.h"
  18. #include "windows.h"
  19.  
  20. #define ASM
  21.  
  22.  
  23. #define hibyte(x)        (((char *) &(x))[0])
  24. #define lobyte(x)        (((char *) &(x))[1])
  25.  
  26. short current_crc;
  27. long hqx_length;
  28. static unsigned char conv_tab[256];
  29. short show_info;
  30. unsigned short max_hqx_header;
  31. short info_file_open=0;
  32. Boolean save_info;
  33. short info_file;
  34. static Boolean EOF_reached;
  35. static short first_in_buffer=0,last_in_buffer=0;
  36. static unsigned char hqxbuf[256];
  37.  
  38. #define RUNCHAR 0x90
  39.  
  40.  
  41. void raise_hqx_error(void);
  42.  
  43.  
  44. void close_info_file()
  45. {
  46. if(info_file_open>0){
  47.     FSClose(info_file);
  48.     }
  49. info_file_open=0;
  50. }
  51.  
  52. short ci_strcmp(p1,p2)
  53. register char *p1,*p2;
  54. /* case-insensitive compare (it's not general purpose: the second string must be
  55. lowercase, and the return value is 0 if equal, 1 if not) */
  56. {
  57. register unsigned char c;
  58. while(*p2){
  59.     if(!(c=*p1++)) return 1;
  60.     if(c>='A'&&c<='Z') c+= 'a'-'A';
  61.     if(c!= *p2++) return 1;
  62.     }
  63. return *p1 != '\0';
  64. }
  65.  
  66. short is_hqx_name()
  67. {
  68. /* returns 0 if the name does not terminate by .hqx, -1 if it does but .hqx is
  69. preceded by "part" (or "p" preceded by a non-letter) followed by one or two digits, 
  70. 1 otherwise.
  71. (obviously, files broken in parts can't be extracted on the fly, and it's customary
  72. to call them contentname.part1.hqx, but we've seen also a_very_long_name.p2.hqx
  73. or long_application_name1.1p3.hqx, and startup10.hqx probably is not a part)
  74. The char comparisons are case-insensitive
  75. */
  76. register short i,l;
  77. static char p[]={'p','a','r','t'};
  78. l=strlen(tarh.name);
  79. if(l <=4 || ci_strcmp(&tarh.name[l-4],".hqx" ) ) return 0;
  80.  
  81. /* se sono qui c'era .hqx, ma se c'è anche part devo non convertire */
  82.     
  83. if( l < 9 || tarh.name[l-5] <'0' || tarh.name[l-5] >'9' ) return 1;
  84. if(tarh.name[l-6]>='0'&&tarh.name[l-6]<='9') l--;
  85.  
  86. if(l<9) return 1;
  87. if( (tarh.name[l-6]|0x20) == 'p' && ((i=tarh.name[l-7]|0x20)<'a'||i>'z') ) return -1;
  88. for(i=0;i<4;i++)
  89.     if( (tarh.name[l-9+i]|0x20) != p[i] ) return 1;
  90. return -1;    /* il .hqx è preceduto da part1 o part2 etc., sarebbe inutile cercare 
  91.                 di convertire */
  92. }
  93.  
  94.  
  95. void check_CRC()
  96. {
  97. /* read the stored CRC value from the BinHex file and compare it with the
  98. computed value
  99. */
  100. short calcolato,letto;
  101.  
  102.  
  103. /* per la verità non sarebbe necessario, il CRC è fatto in modo che se si calcola
  104. su tutti i dati, CRC compreso, il risultato deve essere 0
  105. -- really, the standard CRC (and CCITT CRC is standard) has this property: 
  106. if you don't compute it on 0 twice (remove the two calls) but compute on the 
  107. two CRC bytes (get the value of current_CRC after the read_hqx(¬_care,2) ), 
  108. then current_CRC is 0 if and only if the computation matches the stored CRC. 
  109. But doing it the same way as the originating routine is easier to understand
  110. */
  111. CalcCRC(0);
  112. CalcCRC(0);
  113.  
  114. calcolato=current_crc;
  115. read_hqx(&letto,2);
  116. /*printf("calcolato=%x,letto=%x,corrente=%x len=%ld:%d\n",calcolato,letto,current_crc,
  117.     hqx_length,last_in_buffer-first_in_buffer);*/
  118. if( calcolato!=letto ){
  119.     printf("Invalid CRC\n");
  120.     if(!ignore_errors) raise_hqx_error();
  121.     }
  122. }
  123.  
  124.  
  125.  
  126. short hqx_header()
  127. {
  128. /* read the header of a BinHex file and place all informations in a struct
  129. which follows the MacBinary format.
  130. In the file "hqx-format.txt", available in public domain archives, containing
  131. contributions by Dave Johnson and Tom Coradeschi, one can read:
  132. >The header format consists of a one byte name length, then the mac
  133. >file name, then a null.  The rest of the header is 20 bytes long,
  134. >and contains the usual file type, creator/author, file flags, data
  135. >and resource lengths, and the two byte crc value for the header.
  136.  
  137. */
  138. static char msg[]="Invalid BinHex header\n";
  139.  
  140. EOF_reached=false;
  141. /* handle all the text before the BinHex data. If there is nothing else,
  142. return immediately */
  143. FindStart();
  144.  
  145. if(!hqx_length) return -1;        /* sono già in fondo, capita se non ho trovato l'inizio */
  146.  
  147. current_crc=0;
  148. last_in_buffer=first_in_buffer=0;
  149. read_hqx(&macbinh.nlen,1);            /* file name length */
  150. if(macbinh.nlen==0 || macbinh.nlen>63){
  151.     printf(msg);
  152.     raise_hqx_error();
  153.     }
  154. read_hqx(&macbinh.name,macbinh.nlen);    /* rest of file name */
  155.  
  156. if(read_hqx(&macbinh.zero,1),macbinh.zero!=0 && !ignore_errors){    /* a zero byte */
  157.     printf(msg);
  158.     raise_hqx_error();
  159.     }
  160.  
  161. read_hqx(&macbinh.finfo,10);    /* type, creator, Finder flags etc. */
  162. macbinh.protected=0;
  163. macbinh.zero=0;
  164. read_hqx(&macbinh.dflen,8);        /* data fork & resource fork lengths (two long ints) */
  165. /*printf("sizes=%lx %lx\n",macbinh.dflen,macbinh.rflen);*/
  166.  
  167. /* queste informazioni non c'è bisogno di settarle, basta modificare la routine
  168. che chiama PBSetFInfo in modo che rispetti i valori correnti :
  169. -- the creation and modification dates are missing, but set_binhex will 
  170.     fill those fields
  171.  
  172. *(long*)&macbinh.finfo.fdLocation = 0;
  173. macbinh.finfo.fdFldr=0;
  174. GetDateTime (&macbinh.cdate);
  175. macbinh.mdate=macbinh.cdate;
  176. */
  177.  
  178. check_CRC();
  179. return 0;
  180.  
  181. }
  182.  
  183.  
  184.  
  185. /***************** findstart *********************/
  186.  
  187. void FindStart ()
  188. {
  189. /* routine ispirata a quella (in Pascal) di Peter Lewis, ma perfezionata
  190. -- This routine is vaguely inspired to the Pascal procedure by
  191. Peter Lewis (deHQX.p), but it does not fail if the start string was modified
  192. or deleted from the file
  193. */
  194.  
  195. OSErr oe;
  196. short pos,stat;
  197. unsigned short timeout=file_aperto==ff_binhex?65535:max_hqx_header;
  198. static char startstr[] = "(This file must be converted with BinHex 4.0)";
  199. short oldpos=0;
  200. register short i,b;
  201. Boolean flush_needed;
  202. extern Boolean all_listonly;
  203.  
  204. /* per evitare di scandire centinaia di Kbytes per nulla, ho un "timeout" (20 settori) 
  205. e la possibilità di gestire anche files in cui la scritta di cui sopra è stata tolta, 
  206. col che c'è solo un ":" seguito da uno dei primi codici della stringa hqx (è la 
  207. lunghezza del nome, in teoria max 63 ma in pratica max 31, diviso per 4 per prendere 
  208. solo 6 bit...) i quali codici sono tutti cose strane da stare dopo un : 
  209. -- a ':' at the start of the line must be followed by one of the first codes
  210. in the hqx decoding string (the file name length can't be longer than 12*4+3 =51:
  211. it's not the "official" limit of 63, but ":0" could be meaningful...
  212. The routine is much more complex than it should be due to the explicit 
  213. handling of buffering with both the file and console output 
  214. */
  215.  
  216. show_info = SHOWINFO;
  217. save_info = SAVEINFO;
  218. if(show_info) disable_autoflush(2);
  219. stat=0;
  220. pos = 0;
  221. do{
  222.     b = get_hqx_byte(&flush_needed);
  223.  
  224. /* controllo nel caso manchi la stringa ma ci siano i dati BinHex */
  225.     if(b==LF||b==CR||b=='\f')
  226.         stat=0;
  227.     else if(b==':')
  228.         stat++;
  229.     else if(stat==1 && conv_tab[b]<=12){    /*un ':' a inizio riga seguito da...*/
  230.         unget_char();
  231.         flush_hqx_header(0);
  232.         if( show_info) {
  233.             start_of_line();
  234.             enable_autoflush();
  235.             }
  236.         close_info_file();
  237.         return;
  238.         }
  239.     else if(stat==0 && (b==' '||b=='\t') )
  240.         ;
  241.     else
  242.         stat=2;
  243. /* controllo per il caso non ci siano né stringa né dati BinHex */
  244.     if(--timeout==0){
  245.         char buffer[512];
  246.         start_of_line();
  247.         printf(in_Italia?"Non ho trovato dati BinHex nei primi %u bytes\n":
  248.             "BinHex header not found within %u bytes\n",
  249.             file_aperto==ff_binhex?(short)65535:max_hqx_header);
  250.         if(show_info) enable_autoflush();
  251.  
  252.         if(! save_info)
  253.             raise_hqx_error();
  254.         else{
  255.             flush_hqx_header(0);        /* ora passo in modalita' salvataggio 
  256.                             normale, vorrà dire che mando tutto nel .info 
  257.                             -- BinHex data not found => since the .info file
  258.                             contains a copy of all what was read, continue
  259.                             to fill it, without any further search */
  260.             while(hqx_length>0){
  261.                 if(hqx_length>=512L) i=512; else i= (short)hqx_length;
  262.                 hqx_length -= i;
  263.                 if(readblock(buffer,i)!=0)
  264.                     error_message_1("Disk read error %d\n",err_code);
  265.                 macize_ASCII(buffer,512);
  266.                 if(mac_fwrite(buffer,i,info_file)<0)
  267.                     write_error_message();
  268.                 }
  269.             }
  270.         close_info_file();
  271.         return;
  272.         }
  273. /* controllo presenza stringa */
  274.     if (b == startstr[pos])
  275.         pos ++;
  276.     else
  277.         pos = 0;
  278. /* copia dei caratteri sulla console e/o file .info */
  279.     if(flush_needed || pos==sizeof(startstr)-1){
  280.         if(save_info && oldpos!=0 && pos!=sizeof(startstr)-1)
  281.             mac_fwrite(startstr,oldpos,info_file);    /* è poco pulito farlo così, fidandosi
  282.                 che il file è aperto e contiene quello che deve contenere...*/
  283.  
  284.         if(show_info && oldpos!=0 && pos!=sizeof(startstr)-1){
  285.             for(i=0;i<oldpos;i++)
  286.                 put_char(startstr[i]);    /* la volta prima avevo erroneamente omesso
  287.                             dei caratteri ritenendo fossero parte della start string
  288.                 -- when the buffer had to be flushed, I could have seen a part 
  289.                 of the start string: I didn't save it, but if I'm here than I
  290.                 discovered that it was NOT the start string, hence those chars
  291.                 had to be written out: for the console, do it here, for the .info
  292.                 file it's flush_hqx_header which does that */
  293.             }
  294.         oldpos=pos;
  295.         flush_hqx_header(pos);
  296.         }
  297.  
  298.     }
  299. while( pos < sizeof(startstr)-1);
  300.  
  301. close_info_file();
  302. if(show_info){
  303.     start_of_line();
  304.     enable_autoflush();
  305.     }
  306.  
  307. do{
  308.     b = get_hqx_byte(&flush_needed);
  309.     }
  310. while (b!=':');
  311.  
  312. }
  313.  
  314.  
  315.  
  316. /*
  317. io uso tre buffer: quello di uscita è il solito buffer, quello di ingresso 
  318. contiene il blocco letto dal disco; in più, le conversioni sono fatte a
  319. gruppi di 4 bytes, col che ho un buffer intermedio che contiene solo ...
  320. -- The BinHex conversion is performed in two steps, by two routines
  321. each having its own input buffer
  322. */
  323.  
  324. void init_hqx_tab()
  325. /* initialize the conversion table: need be called only once */
  326. {
  327. static unsigned char hqxchars[]=
  328.  "!\"#$%&\'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
  329. register short i;
  330. for( i= 0 ;i<=255; i++)
  331.     conv_tab[i] = 255;
  332. for (i = 0;i<64;i++)
  333.     conv_tab[hqxchars[i]]=i;
  334. conv_tab[LF]=conv_tab[CR]=conv_tab[' ']=conv_tab['\t']=conv_tab['\f']= 254;
  335. }
  336.  
  337.  
  338.  
  339. void read_hqx(destination,nbytes)
  340.  
  341. /* in suntar 1.2, this routine was changed, optimizing for speed: the 
  342. loop present in the callers was moved here, so that register variables
  343. may hold their values for a while, and are not swapped out of the register
  344. after being used just a couple of times */
  345.  
  346. register char *destination;
  347. short nbytes;
  348. /* last conversion stage: it handles the RLL encoding in the BinHex format.
  349. From the file "hqx-format.txt":
  350.  
  351. >There is some run length encoding, where the character to be repeated
  352. >is followed by a 0x90 byte then the repeat count.  For example, ff9004
  353. >means repeat 0xff 4 times.  The special case of a repeat count of zero
  354. >means it's not a run, but a literal 0x90.  2b9000 => 2b90.
  355.  
  356. >*** Note: the 9000 can be followed by a run, which means to repeat the
  357. >0x90 (not the character previous to that).  That is, 2090009003 means
  358. >a 0x20 followed by 3 0x90's.
  359. */
  360.  
  361. {
  362. register unsigned char b;
  363.  
  364. register unsigned char *first=&hqxbuf[first_in_buffer];
  365. register short k=last_in_buffer-first_in_buffer;
  366.  
  367. while(nbytes--){
  368.     if( k < 4 ){
  369.         /* buffer quasi vuoto: riporta quello che resta in testa e riempi di nuovo
  370.         -- refill the buffer by shifting what remains and calling
  371.         read_3_hqx until the buffer is full or the hqx data is over
  372.         */
  373.         mcopy(hqxbuf,first,k);
  374.         first=hqxbuf;
  375.         k += read_3_hqx(&hqxbuf[k],sizeof(hqxbuf)-k);
  376.         last_in_buffer =k;
  377.         if( k<=0 ){
  378.             beep_in_foreground();
  379.             start_of_line();
  380.             printf("hqx: EOF reached\n");
  381.             raise_hqx_error();
  382.             }
  383.         }
  384.     /* sembra troppo semplice per quello che deve fare, ma ho studiato tutti i casi,
  385.     compresi i run di RUNCHAR e la coppia di RUNCHAR, scritto che si deve fare e fatto
  386.     il merge delle azioni comuni fra più rami, e questo è il risultato
  387.     -- I decrement the run length count directly in the input buffer, without
  388.     extracting the RLL code from the buffer until the count goes to 0.
  389.     OK, it's not immediately clear that these instructions work: but I considered
  390.     all possible cases and did a flow graph of the resulting operations, than began
  391.     to merge equal boxes at the end of different paths: I've got a lot of merging
  392.     and the result is this very simple set of instructions */
  393.     b=*first;    
  394.     if(b==RUNCHAR){    /* and first[1] surely 0 */
  395.         if(k <4 || first[2]!=RUNCHAR || !first[3] ){
  396.                 first += 2; k -=2;
  397.                 }
  398.         else
  399.             if ( --first[3] <=1 ) {
  400.                 /* see below */
  401.                 if(first[3]==0){
  402.                     first += 4; k -= 4;
  403.                     }
  404.                 else{
  405.                     first+=2; k-=2;
  406.                     first[1]=0;
  407.                     }
  408.                 }
  409.         }
  410.     else{
  411.         if (k <3 || first[1]!=RUNCHAR || !first[2]){
  412.             first++; k--;
  413.             }
  414.         else{
  415. /* run length may be repeated, for example A4 90 FF 90 FF 90 0A means A4 is 
  416. repeated 520 times. Unfortunately, suntar 1.1 did not handle those cases correctly */
  417.             if(  --first[2] <=1 ){
  418.                 if(first[2]==0){
  419.                     first+=3; k-=3;
  420.                     }
  421.                 else{    /* must copy the char to be repeated so that if there is
  422.                         a further run length, it appears to be applied to it; it's
  423.                         easier to exit from the decrement state when the count is 1
  424.                         and copy anyway than exit when it's 0 and copy only if there
  425.                         is a further run length */
  426.                     first+=2; k-=2;
  427.                     *first=b;
  428.                     }
  429.                 }
  430.             }
  431.         }
  432.  
  433.     *destination++ = b;
  434.  
  435. #ifndef ASM
  436.     CalcCRC(b);
  437. #else
  438.     /* save parameter passing + function call */
  439.     asm{
  440.  
  441.         move.w    current_crc,d0
  442.  
  443.         move.w    #7,d2
  444.         move.w    #0x1021,d1    ; CRC CCITT
  445.     loop:
  446.         lsl.b    #1,b
  447.         roxl.w    #1,d0
  448.         bcc.s    @noxor
  449.         eor.w    d1,d0
  450.     noxor:
  451.         dbra    d2,@loop
  452.  
  453.         move.w    d0,current_crc
  454.         }
  455. #endif
  456.  
  457.  
  458.     }
  459. first_in_buffer = first - hqxbuf;
  460. }
  461.  
  462.  
  463. static char UNEXP_EOF[]="End of BinHex file unexpectedly reached\n";
  464.  
  465. #define FAST_GET_HQX_BYTE(b)    \
  466. if(!more_in_bytes)                \
  467.     b=(unsigned char)get_hqx_byte(&dummy);    \
  468. else{                            \
  469.     if((--hqx_length)<0){        \
  470.         printf(UNEXP_EOF);        \
  471.         raise_hqx_error();        \
  472.         }                        \
  473.     b= (unsigned char)disk_buffer[511-(--more_in_bytes)];\
  474.     }
  475.  
  476.  
  477.  
  478.  
  479. extern short more_in_bytes;
  480.  
  481.  
  482. short read_3_hqx(obuf,maxsize)
  483. /* this one too was optimazed by moving here the loop which
  484. was in the caller */
  485.  
  486. register unsigned char *obuf;
  487. register short maxsize;
  488. {
  489. /* read four BinHex characters and convert them to three bytes
  490. From the file "hqx-format.txt":
  491. >The first and last characters are each a ':'.  After the first ':',
  492. >the rest of the file is just string of 6 bit encoded characters.
  493. >All newlines and carriage returns are to be ignored.
  494. That is, data is sliced in 6-bit pieces, and 3 bytes (24 bits) yield 4
  495. pieces, each encoded by a printable ASCII char (0='!' etc., see the
  496. conversion table)
  497. */
  498.  
  499. Boolean dummy;
  500. unsigned char ibuf[4];
  501. register short i,b;
  502. register short nbytes;
  503. register unsigned char *pti;
  504. short n_errors=0;
  505. static char msg[]="Error: not a BinHex character (dec %d)\n";
  506.  
  507. nbytes=0;
  508. maxsize -= 3;
  509.  
  510. while(maxsize>=0 && !EOF_reached){
  511.  
  512.     nbytes += 3;
  513.     pti=ibuf;
  514.     for(i=0;i<4;i++,pti++){
  515.         do{
  516.             FAST_GET_HQX_BYTE(b)
  517.             }
  518.         while((*pti=conv_tab[b])==(unsigned char)254 );
  519.             /* I ignore spaces too: hqx-format.txt does not tell about them,
  520.             but we happened to download a file which did have spaces,
  521.             and neither Stuffit nor Compact Pro succeeded to extract it,
  522.             only Peter Lewis' DeHQX did the job. Now, suntar too does extract
  523.             that file */
  524.         if( *pti==(unsigned char)255){
  525.             if(b==':'){
  526.                 EOF_reached=true;
  527.                 nbytes += ((i+i+i)>>2)-3;    /* i*6/8 -3 */
  528.                 for(;i<4;i++)
  529.                     ibuf[i]=0;
  530.                 break;
  531.                 }
  532.              printf(msg,b);
  533.              if(!ignore_errors || ++n_errors>=10)
  534.                   raise_hqx_error();
  535.               else
  536.                   i--,pti--;    /* or goto the start of this for loop */
  537.               }
  538.           }
  539. /*printf("<%x %x %x %x>",ibuf[0],ibuf[1],ibuf[2],ibuf[3]);*/
  540. #ifndef ASM
  541.     *obuf++ = ((ibuf[0] << 2) | (ibuf[1] >> 4));    /* these three instructions */
  542.     *obuf++ = ((ibuf[1] << 4) | (ibuf[2] >> 2));     /* are from Dave Johnson's xbin.c */
  543.     *obuf++ = ((ibuf[2] << 6) | ibuf[3]);            /* (for UNIX machines) */
  544. #else
  545. /* no speed gain, but since I've written and tested it, I prefer to use it... */
  546.     asm{
  547.         lea        ibuf,a0
  548.         move.b    (a0)+,d0
  549.         lsl.b    #2,d0
  550.         move.b    (a0)+,d1
  551.         move.b    d1,d2
  552.         lsr.b    #4,d1
  553.         or.b    d1,d0
  554.         move.b    d0,(obuf)+
  555.         lsl.b    #4,d2
  556.         move.b    (a0)+,d1
  557.         move.b    d1,d0
  558.         lsr.b    #2,d1
  559.         or.b    d1,d2
  560.         move.b    d2,(obuf)+
  561.         lsl.b    #6,d0
  562.         or.b    (a0),d0
  563.         move.b    d0,(obuf)+
  564.         }
  565. #endif
  566.  
  567. /*printf("{%x %x %x}\n",obuf[0],obuf[1],ibuf[2]);*/
  568.     maxsize -= 3;
  569.     }
  570. return nbytes;
  571. }
  572.  
  573.  
  574. unsigned char get_hqx_byte(flush_needed)
  575. /* I could implement it by calling readblock, but the hqx decoding has already
  576. so much overhead for each byte that I thought that any instructions saving
  577. was welcome, hence I copied the body of readblock deleting instructions which
  578. are useless when reading one byte at a time
  579. */
  580. Boolean *flush_needed;
  581. {
  582. short i;
  583. if((--hqx_length)<0){
  584.     beep_in_foreground();
  585.     start_of_line();
  586.     printf(UNEXP_EOF);
  587.     raise_hqx_error();
  588.     }
  589.  
  590. if(more_in_bytes==0){
  591.     *flush_needed=false;
  592. /* le stesse cose che fa readblock... */
  593.     if(bar_archive)
  594.         bar_check_floppy_swap(0);
  595.     else
  596.         tar_check_floppy_swap(0);
  597.  
  598.     leggi_settore(sect_n,&disk_buffer);
  599.  
  600.      if((i=check_error_and_events())<0) raise_error();
  601.      if(i>0){    /* "missing disk" was converted to a pause event, now the disk
  602.                  should be in, but I must repeat the operation */
  603.         leggi_settore(sect_n,&disk_buffer);
  604.          if(check_error()) raise_error();
  605.          }
  606.     more_in_bytes=511;
  607.     sect_n++;
  608.     settori_passati++;
  609.     }
  610. else
  611.     *flush_needed= !--more_in_bytes;
  612. return disk_buffer[511-more_in_bytes];
  613. }
  614.  
  615.  
  616. short enough_space_for_info(void);
  617. static short enough_space_for_info()
  618. {
  619. short vrefnum;
  620. long l;
  621. short i;
  622.  
  623.     if(curr_vrefnum)
  624.         vrefnum=curr_vrefnum;
  625.     else{
  626.         volumeParam param;
  627.         char buffer[50];
  628.         param.ioNamePtr=buffer;
  629.         if(PBGetVolSync (¶m) !=noErr) return 0;
  630.         vrefnum=param.ioVRefNum;
  631.         }
  632.     if(GetVInfo (0,NULL,&vrefnum,&l)!=noErr)
  633.         return 0;
  634.     if( l >= min(10240L,hqx_length+1024L))    /* I don't know the size, but it can't
  635.                         be bigger than the whole file (hqx_length was already
  636.                         decremented, hence I add something to it) and very rarely
  637.                         it's bigger than 10 K */
  638.         return 0;
  639.  
  640.     for(;;){
  641.         extern char *titoli_full[];
  642.         i=semimodalDialog(141,NULL,NULL,titoli_full,3,
  643.                in_Italia?"\pSpazio su disco scarso: puoi voler cancellare\r\
  644. qualcosa prima di cliccare su un bottone\roppure puoi abortire il comando":
  645. "\pScarce space on destination volume: you\r\
  646. might wish to delete something before clicking a\r\
  647. button or you may abort the current command",
  648.                NULL,NULL,teJustCenter,true,NULL);
  649.         if(i==2){
  650.              /* continue elsewhere */
  651.             select_directory();
  652.             if(reply.good) return 0;
  653.             }
  654.         else if(i==3)    /* skip */
  655.             return 1;
  656.         else
  657.             return 0; /* continue here: maybe the user deleted some files,
  658.                     or thinks that this .info file is smaller than the available
  659.                     space */
  660.         }
  661. }
  662.  
  663. void open_info_file()
  664. /* opens the .info file: that includes building its name... */
  665. {
  666. char name_buffer[102];
  667. register short i,l;
  668. char *hqx_name=bar_archive ? ((barh_type*)ultimo_header)->name : 
  669.     ((struct tarh_type*)ultimo_header)->name;
  670. extern OSType filecreator,filetype;
  671. extern Boolean devo_chiudere_out;
  672. extern IOParam pb;
  673.  
  674. /* compute the name */
  675.  
  676. l=strlen(hqx_name);
  677. strcpy(name_buffer,hqx_name);
  678.  
  679. if(l>4 && !ci_strcmp(&name_buffer[l-4],".hqx")){
  680.     for(i=l-1;i>=0 && hqx_name[i]!='/';i--)
  681.         ;
  682.     /* ora i è -1 o punta ad un '/', quindi la lunghezza del nome è : */
  683.     i=l-i-1;
  684.     strcpy(&name_buffer[l-3], i>=31 ? "inf" : "info");
  685.     }
  686. else    /* if opened by Open File, it may not contain .hqx */
  687.     strcat(name_buffer,".info");
  688. unix_to_mac(name_buffer);
  689.  
  690. filecreator=text_creator;
  691. filetype='TEXT';
  692.  
  693. /* now, check for enough space (must have a private routine for that since
  694. I don't know how long the .info file will be) */
  695.  
  696. if(enough_space_for_info()){
  697.     info_file_open=-1;        /* skip this file... */
  698.     return;
  699.     }
  700.  
  701. /* finally, open the file ! */
  702.  
  703. if(create_file(name_buffer,fsWrPerm,false)==0){
  704.     devo_chiudere_out=false;
  705.     info_file=pb.ioRefNum;
  706.     info_file_open=1;
  707.     }
  708. else
  709.     info_file_open=-1;    /* duplicate name + cancel: don't create an info file ! */
  710. }
  711.  
  712.  
  713. void flush_hqx_header(pos)
  714. short pos;
  715. /* it's rather complex: it must write data to the console and the .info
  716. file, but both of them may be disabled.
  717. Furthermore, it's called when the buffer is officially empty, but obviously 
  718. its bytes still contain the characters which FindStart has read and discarded
  719. since they were not the start of the BinHex data
  720. */
  721. {
  722. /* attenzione, faccio affidamento sul fatto che nel buffer ci restano i bytes letti,
  723. e che grazie all'ubbidienza al parametro flush_needed della routine precedente 
  724. quelli non ancora salvati partono all'offset 0 */
  725.  
  726. register short i,j;
  727. register char last_char;
  728. if( ! save_info && ! show_info ) return;
  729.  
  730. j=512-more_in_bytes-pos;
  731. if(!j) return;
  732. if(show_info==1 && j>100)
  733.     printf("=============== hqx info ===============\n\n");    /* la prima volta */
  734. if(show_info!=0){
  735.     show_info++;
  736.     disable_autoflush(2);    /* in alcuni casi un messaggio (nuovo disco...) può averlo
  737.                             abilitato */
  738.     }
  739.  
  740. for(i=0;i<j;i++){    /* butto fuori il contenuto del buffer, ma 
  741.                         togliendo un eventuale inizio della start string
  742.                     -- pos!=0 means that the last pos chars might be part of the
  743.                     start string, I must not write them
  744.                     */
  745.     last_char=disk_buffer[i];
  746.     if(show_info) put_char(last_char);
  747.     if((last_char==CR || last_char==LF) && show_info>=4 && more_in_bytes==0){
  748.         printf(in_Italia?"... (testata lunga, non mostrata per intero)\n\n\n":
  749.         "... (long header, further lines are not shown)\n\n\n");
  750.         enable_autoflush();
  751.         show_info=0;
  752.         }
  753.     }
  754.  
  755. if( save_info && j>0){
  756.     if(!info_file_open) open_info_file();    /* the info file is opened on the fly
  757.             here, since FindStart did not open it: if no text precedes the
  758.             BinHex data, the info file is not created */
  759.     if(info_file_open>0){
  760.         macize_ASCII(disk_buffer,j);
  761.         if(mac_fwrite(disk_buffer,j,info_file)<0){
  762.             beep_in_foreground();
  763.             write_error_message();
  764.             }
  765.         }
  766.     }
  767. }
  768.  
  769. void hqx_end_of_file()
  770. {
  771. hqx_length -= more_in_bytes;    /* caution: the buffer may contain up to 511 bytes
  772.         when hqx_length is 0, and hqx_length may be -1 (it's decremented before
  773.         testing it) hence the result may be negative */
  774. more_in_bytes=0;
  775. if(hqx_length>0)
  776.     sect_n+= (hqx_length+511) >>9;
  777. hqx_length=0;
  778. }
  779.  
  780. static void raise_hqx_error()
  781. /* handles the hqx decoding error by skipping the rest of the file and closing
  782. the open files so that untar_hqx will be able to return regularly, and the
  783. extraction or list will continue with next file */
  784. {
  785. close_or_del_out_file();
  786. close_info_file();
  787. enable_autoflush();
  788.  
  789. hqx_end_of_file();
  790.  
  791. if(fase==reading_disk&&file_aperto!=ff_binhex || fase==selected_reading || fase==hack_listing){
  792.     if(! (fase==reading_disk&&listonly || fase==hack_listing) ){
  793.         one_empty_line();
  794.         printf(in_Italia?"****** Estrazione BinHex interrotta":
  795.         "****** BinHex extraction was aborted");
  796.         printf(" ******\n\n");
  797.         }
  798.     longjmp(main_loop,-2);
  799.     }
  800. else
  801.     longjmp(main_loop,-1);
  802.  
  803. }
  804.  
  805.  
  806. /* assembly language is faster: since you must repeat the body of the loop 
  807. for each BIT (not byte !) of the file, a fast CRC routine is essential if
  808. your BinHex routine must not been very slow... */
  809. #ifdef ASM
  810.  
  811. void CalcCRC(current_byte)
  812. char current_byte;
  813. /* from calcCRC.a in the deHQX source code by Peter Lewis, adapting
  814. it to the C parameter passing conventions and the Think-C half-assembly-half-C
  815. syntax
  816. */
  817. {
  818. asm{
  819.  
  820.     move.w    current_crc,d0
  821.     move.b    current_byte,d1
  822.     
  823.     move.w    #7,d2
  824. loop:
  825.     lsl.b    #1,d1
  826.     roxl.w    #1,d0
  827.     bcc.s    @noxor
  828.     eor.w    #0x1021,d0    ; CRC CCITT
  829. noxor:
  830.     dbra    d2,@loop
  831.     move.w    d0,current_crc
  832.     }
  833. }
  834.  
  835. #else
  836.  
  837. void CalcCRC (v)
  838. {
  839. short temp;
  840. register short     i;
  841.  
  842.     for (i = 1;i<=8;i++){
  843.         temp = current_crc&0x8000;
  844.         current_crc = (current_crc<<1) | (v>>7)&1 ;
  845.         if( temp )
  846.             current_crc ^= 0x1021;
  847.         v <<= 1;
  848.         }
  849. }
  850. #endif
  851.  
  852.